<template>
  <template v-if="show"
    ><div class="colRight-header">
      {{ chatDatas?.LatestMessInfo.remark }}
      <span class="colRight-header__inputing" v-show="messageStore.isInputing"
        >对方正在输入...</span
      >
    </div>
    <div class="colRight-messages__main">
      <el-scrollbar ref="elScrollBarRef" @scroll="scroll">
        <message-record
          ref="messRecordRef"
          :data="chatDatas.messagesDatas"
          :other-id="userId"
          :is-last="chatDatas.isLast"
          :scroll-top="scrollTop"
          @scroll-to-top="scrollToTop"
          @updateScroll="updateScroll"
          @retry-send-message-txt="retrySendMessageTxt"
          @retry-send-message-record="retrySendMessageRecord"
        ></message-record>
      </el-scrollbar>
    </div>
    <div class="colRight-messages__footer">
      <message-edit
        ref="messEdit"
        v-model="chatDatas.editMessage"
        @send-message="sendMessage"
        :other-id="userId"
        :self-id="self.userId"
        @disabled="isDisabled"
      ></message-edit>
      <div class="message-send">
        <button
          :class="{
            'send-button': true,
            'send-button__active': !disabled,
            'send-button__disabled': disabled,
          }"
          @click="sendMessage"
        >
          发送
        </button>
      </div>
    </div></template
  >
  <template v-else>
    <feed-back
      :is-requesting="isRequesting"
      :is-error="isError"
      message="请求出错啦"
      @retry="requestChatRecord(userId)"
    ></feed-back>
  </template>
</template>

<script lang="ts" setup>
import { ref, watch, computed, provide, readonly } from "vue";
import messageEdit from "./edit/index.vue";
import messageRecord from "./detailMessage/index.vue";
import { source, refreshAxios } from "@/plugins/http";
import { useRouter, onBeforeRouteLeave } from "vue-router";
import { useUserPinia } from "@/pinia/user";
import { useMessagePinia } from "@/pinia/messages";
import { storeToRefs } from "pinia";
import { ElMessage } from "element-plus";
import type { ElScrollbar } from "element-plus";
import feedBack from "@/components/ui-components/feedBack.vue";
import { dataURItoBlob } from "@/plugins/pictureProcess";

const props = defineProps<{
  userId: string;
}>();
const router = useRouter();
const messageStore = useMessagePinia();
const { user: self } = storeToRefs(useUserPinia());

// 对应聊天对象
const chatDatas = computed(() => messageStore.chatList.get(props.userId)!);
/**
 * 是否显示对应的聊天记录
 */
const show = ref<boolean>(false);
/**
 * 是否正在请求聊天记录
 */
const isRequesting = ref<boolean>(false);
/**
 * 请求是否出错
 */
const isError = ref<boolean>(false);
// 是否正在建立会话
const isSetConver = ref<boolean>(false);
// 请求重新建立会话的次数
const setConverCount = ref<number>(0);
// 记录当前一次会话
const setConversation = (payload?: { selfId: string; otherId: string }) => {
  // 取消上一次请求
  if (isSetConver.value) {
    source.cancel();
    refreshAxios();
    isSetConver.value = false;
  }
  isSetConver.value = true;
  messageStore
    .setConversation(payload)
    .then((code: number) => {
      isSetConver.value = false;
      if (code !== 1 && setConverCount.value < 4) {
        setConversation(payload);
        setConverCount.value++;
      }
    })
    .catch(() => (isSetConver.value = false));
};

onBeforeRouteLeave(() => setConversation());

// 请求聊天记录
const requestChatRecord = (userId: string) => {
  // 取消上一次请求
  if (isRequesting.value) {
    source.cancel();
    refreshAxios();
    isRequesting.value = false;
  }
  if (isError.value) isError.value = false;
  if (show.value) show.value = false;
  isRequesting.value = true;
  messageStore
    .getPartMessagesInit({ userId, isHaveWindow: messageStore.chatList.has(userId) })
    .then((code: number) => {
      isRequesting.value = false;
      if (code === 1) show.value = true;
      else if (code === 0) isError.value = true;
      else if (code === -2)
        // 表示对方并未是自己的好友，请求失败
        router.push("404");
    })
    .catch(() => {
      isRequesting.value = false;
      isError.value = true;
    });
};

watch(
  () => props.userId,
  (userId) => {
    const datas = messageStore.chatList.get(userId);
    if (messageStore.isRequChatLisFinsh) {
      // 如果请求没请求过消息记录的话
      if (!datas?.isRequMessRecord) requestChatRecord(userId);
      else {
        // 清空未读消息数
        datas.LatestMessInfo.unReadMessCou = 0;
        if (!show.value) show.value = true;
      }
    } else {
      const timing = setInterval(() => {
        // 如果请求消息聊天列表结束
        if (messageStore.isRequChatLisFinsh) {
          clearInterval(timing);
          if (!datas?.isRequMessRecord) requestChatRecord(userId);
        }
      }, 10);
    }
    setConversation({ selfId: self.value.userId, otherId: userId });
  },
  {
    immediate: true,
  }
);

/**
 * 给编辑组件一个ref属性，便于通过$refs访问
 */
const messEdit = ref<typeof messageEdit>();
/**
 * 给消息记录的滚动条组件一个ref属性
 */
const elScrollBarRef = ref<typeof ElScrollbar>();
/**
 * 给消息记录组件一个ref属性
 */
const messRecordRef = ref<typeof messageRecord>();

// 消息记录滚动条滚动到底部
const scrollToTop = (top: number) => elScrollBarRef.value!.setScrollTop(top);
// 更新滚动条状态
const updateScroll = () => elScrollBarRef.value?.update();

// 发送消息时开始发送请求
const sendMessageRequest = (payload: {
  retry: boolean;
  content: string;
  id: string;
  imgs: Array<string>;
}) => {
  return new Promise((resolve) => {
    const chatDatasTarget = chatDatas.value!.messagesDatas.get(payload.id);
    if (payload.retry) {
      chatDatasTarget!.isLoading = true;
      chatDatasTarget!.isError = false;
    }
    messageStore
      .sendMessage({
        selfId: self.value.userId,
        otherId: props.userId,
        content: payload.content,
        imgs: payload.imgs,
        id: payload.id,
      })
      .then((code: number) => {
        if (code === 1) {
          // 滚动条滚动到底部
          setTimeout(() => {
            updateScroll();
            (messRecordRef.value as any).scrollBottom();
          }, 50);
          resolve(1);
        } else {
          ElMessage({
            type: "error",
            duration: 1500,
            message: code === -2 ? "发送失败，对方和自己不是好友关系" : "发送失败",
          });
          if (chatDatasTarget) {
            chatDatasTarget.isError = true;
            chatDatasTarget.isLoading = false;
          }
        }
        !payload.retry && messEdit.value?.afterSendFocus();
      })
      .catch(() => {
        if (chatDatasTarget) {
          chatDatasTarget.isError = true;
          chatDatasTarget.isLoading = false;
        }
        ElMessage({
          type: "error",
          duration: 1500,
          message: "发送失败",
        });
        !payload.retry && messEdit.value?.afterSendFocus();
      });
  });
};

// 当消息发送失败时重新发送(限于文本消息)
const retrySendMessageTxt = (id: string, content: string) => {
  const divEle: HTMLDivElement = document.createElement("div");
  let blobUrl: string = "";
  let imgFilesBase64s: string[] = [];
  if (/<img.*?(?:>|\/>)/gi.test(content)) {
    divEle.innerHTML = content;
    const imgsEle = Array.from(divEle.querySelectorAll("img"));
    imgsEle.forEach((item) => {
      if (
        item.dataset.key !== "表情" &&
        item.src.search(/[data:image/*;base64,]/) === 0
      ) {
        imgFilesBase64s.push(item.src);
        blobUrl = URL.createObjectURL(dataURItoBlob(item.src));
        item.src = blobUrl;
      }
    });
  }
  sendMessageRequest({
    retry: true,
    id,
    imgs: imgFilesBase64s,
    content: divEle.innerHTML ? divEle.innerHTML : content,
  }).then(() => URL.revokeObjectURL(blobUrl));
};

/**
 * 是否禁止发送消息
 */
const disabled = ref<boolean>(true);
const isDisabled = (boo: boolean) => (disabled.value = boo);

/**
 * 开始发送消息
 */
const sendMessage: () => void = (): void => {
  const content: string = chatDatas.value!.editMessage!;
  let blobUrl: string = "";
  if (!disabled.value) {
    let currentContent: string = content;
    let divEle: HTMLDivElement = document.createElement("div");
    let imgFilesBase64s: string[] = [];
    const imgReg = content.match(/<img.*?(?:>|\/>)/gi);
    if (imgReg?.length) {
      divEle.innerHTML = currentContent;
      const imgsEle = Array.from(divEle.querySelectorAll("img"));
      imgsEle.forEach((item) => {
        if (item.dataset.key !== "表情") {
          item.setAttribute("class", "chat-message__img");
          if (item.src.search(/[data:image/*;base64,]/) === 0) {
            imgFilesBase64s.push(item.src);
            blobUrl = URL.createObjectURL(dataURItoBlob(item.src));
            item.src = blobUrl;
          }
        }
      });
      currentContent = divEle.innerHTML;
      // 将[]符号实体化
      currentContent = currentContent.replaceAll("[", "&#91;").replaceAll("]", "&#93;");
      for (const imgStr of imgReg) {
        divEle.innerHTML = imgStr;
        const imgEle = divEle.querySelector("img");
        if (imgEle?.dataset.key === "表情")
          currentContent = currentContent.replaceAll(imgStr, `[${imgEle?.alt}]`);
      }
      divEle = null as any;
    }
    // 将[]符号实体化
    else
      currentContent = currentContent.replaceAll("[", "&#91;").replaceAll("]", "&#93;");
    chatDatas.value!.editMessage = "";
    disabled.value = true;
    const timestamp: string = new Date().getTime() + "";
    chatDatas.value!.messagesDatas.set(timestamp, {
      userId: self.value.userId,
      isLoading: true,
      message: currentContent,
      isError: false,
      avatar: self.value.avatar,
      type: "text",
    });
    // 滚动条滚动到底部
    setTimeout(() => (messRecordRef.value as any).scrollBottom());
    sendMessageRequest({
      retry: false,
      id: timestamp,
      imgs: imgFilesBase64s,
      content: currentContent,
    }).then(() => URL.revokeObjectURL(blobUrl));
  }
  if (!content) messEdit.value?.afterSendFocus();
};

let scrollTop = ref<number>(0);
// 滚动消息详情的滚动条触发的动作
const scroll = ({ scrollTop: scrolltop }: { scrollTop: number }) => {
  messRecordRef.value!.closeMenu();
  messRecordRef.value!.closeUserInfo();
  scrollTop.value = scrolltop;
};

// 重发录音信息
const retrySendMessageRecord = async (payload: {
  content: string;
  duration: number;
  id: string;
}) => {
  const { content, duration, id } = payload;
  try {
    const file: Blob = await fetch(content).then((res) => res.blob());
    const chatDatasTarget = chatDatas.value!.messagesDatas.get(id);
    if (chatDatasTarget) {
      chatDatasTarget.isLoading = true;
      chatDatasTarget.isError = false;
    }
    messageStore
      .sendRecordMessage({
        otherId: props.userId,
        file,
        duration,
        id,
      })
      .then((code) => {
        if (code === 1) {
          // 释放内存
          URL.revokeObjectURL(content);
          // 滚动条滚动到底部
          setTimeout(() => {
            updateScroll();
            (messRecordRef.value as any).scrollBottom();
          }, 50);
        } else {
          ElMessage({
            type: "error",
            duration: 1500,
            message: code === -2 ? "发送失败，对方和自己不是好友关系" : "发送失败",
          });
          if (chatDatasTarget) {
            chatDatasTarget.isError = true;
            chatDatasTarget.isLoading = false;
          }
        }
      })
      .catch(() => {
        if (chatDatasTarget) {
          chatDatasTarget.isError = true;
          chatDatasTarget.isLoading = false;
        }
        ElMessage({
          type: "error",
          duration: 1500,
          message: "发送失败",
        });
      });
  } catch (error) {
    console.log(error);
  }
};

/**
 * 对方的id值
 */
const otherId = ref<string>(props.userId);
watch(
  () => props.userId,
  (newId: string) => (otherId.value = newId)
);
provide("scrollBottom", () => {
  (messRecordRef.value as any).scrollBottom();
});
provide("updateScroll", updateScroll);
provide("otherId", readonly(otherId));
</script>

<style lang="scss" scoped>
.colRight {
  &-header {
    position: relative;
    @include centerVertically(50px);
    border-bottom: 1px solid $splitColor;
    box-sizing: border-box;
    font-size: 18px;
    padding-left: 25px;

    .spin {
      position: absolute;
      right: 3%;
      top: 25%;
      font-size: 25px;
      animation: loading-rotate__spinMess 1s linear infinite;
    }

    &__inputing {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 14px;
      color: rgba(107, 107, 107, 0.692);
    }
  }

  &-messages {
    &__main {
      height: 310px;
      border-bottom: 1px solid $bottomBorCor;
      box-sizing: border-box;
    }

    &__footer {
      height: 140px;
      box-sizing: border-box;
      background-color: rgba(243, 240, 240, 0.114);

      .message-send {
        @include centerVertically(50px);
        text-align: right;

        .send-button {
          padding: 8px 25px;
          margin-right: 30px;
          @include noselect;
          border-radius: 3px;
          border: 0;

          @mixin thame_button($cursor, $color, $bag) {
            cursor: $cursor;
            color: $color;
            background-color: $bag;
          }

          &__active {
            @include thame_button(pointer, #333, rgba(214, 209, 209, 0.509));
            transition: background-color 0.1s ease-in;

            &:hover {
              background-color: rgba(169, 167, 167, 0.509);
              transition: background-color 0.35s ease-out;
            }

            &:active {
              background-color: rgba(115, 114, 114, 0.509);
            }
          }

          &__disabled {
            @include thame_button(
              not-allowed,
              rgba(51, 51, 51, 0.7),
              rgba(236, 235, 235, 0.509)
            );
          }
        }
      }
    }
  }
}
</style>
